/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-11-21     SummerGift   first version
 * 2018-11-22     flybreak     Make the first version of pcf8575's package
 */

#include <rtthread.h>
#include <rtdevice.h>
#include "pcf8575.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME     "pcf8575"
#define DBG_LEVEL            DBG_INFO
#define DBG_COLOR
#include <rtdbg.h>

static pcf8575_device_t io_pcf8575_dev;
#define I2C_BUS    "i2c1"

void PCF8575_Init()
{
    io_pcf8575_dev = pcf8575_init(I2C_BUS, RT_NULL);
}

pcf8575_device_t io_ctrl_init()
{
    pcf8575_device_t dev = RT_NULL;
    RT_ASSERT(I2C_BUS);
    dev = rt_calloc(1, sizeof(struct pcf8575_device));
    if (dev == RT_NULL)
    {
        LOG_E("Can't allocate memory for pcf8575 device on '%s' ", I2C_BUS);
        goto __exit;
    }
    dev->bus = (struct rt_i2c_bus_device *)rt_device_find(I2C_BUS);
    if (dev->bus == RT_NULL)
    {
        LOG_E("i2c_bus %s for PCF8575 not found!", I2C_BUS);
        goto __exit;
    }
    dev->i2c_addr = PCF8575_ADDR_DEFAULT;
    if (rt_device_open(&dev->bus->parent, RT_NULL) != RT_EOK)
    {
        LOG_D("i2c_bus %s for PCF8575 opened failed!", I2C_BUS);
        goto __exit;
    }
    LOG_D("pcf8575 init done", dev_name);
    return dev;

__exit:
    if (dev != RT_NULL)
        rt_free(dev);

    return RT_NULL;
}

void io_ctrl_deinit(pcf8575_device_t dev)
{
    pcf8575_deinit(dev);
}

void io_ctrl(pcf8575_device_t dev,rt_uint8_t dirver_num,rt_uint8_t value)
{
    pcf8575_pin_write(dev,dirver_num-1,value);
}

uint16_t pcf8575_port_read(pcf8575_device_t dev)
{
    uint16_t value;
    uint8_t recv_value[2];
    rt_device_read(&dev->bus->parent, dev->i2c_addr, recv_value, 2);
    value = recv_value[0];
    value |= ((uint16_t)recv_value[1])<<8;
    return value;
}

void pcf8575_port_write(pcf8575_device_t dev, uint16_t value)
{
    uint8_t write_value[2];
    write_value[0] = (uint8_t)value;
    write_value[1] = (uint8_t)(value>>8);
    rt_device_write(&dev->bus->parent, dev->i2c_addr, write_value, 2);
}

uint8_t pcf8575_pin_read(pcf8575_device_t dev, uint16_t bit)
{
    uint16_t data;
    data = pcf8575_port_read(dev);

    if (data & (1 << bit))
        return 1;
    else
        return 0;
}

void pcf8575_pin_write(pcf8575_device_t dev, uint16_t bit, uint8_t value)
{
    uint16_t data;
    data = pcf8575_port_read(dev);

    if (value == 0)
        data &= ~(1 << bit);
    else 
        data |= 1 << bit;

    pcf8575_port_write(dev, data);
}

pcf8575_device_t pcf8575_init(const char *dev_name, rt_uint8_t i2c_addr)
{
    uint16_t buffer[] = { 0x0000 };
    pcf8575_device_t dev = RT_NULL;
    
    RT_ASSERT(dev_name);

    dev = rt_calloc(1, sizeof(struct pcf8575_device));
    if (dev == RT_NULL)
    {
        LOG_E("Can't allocate memory for pcf8575 device on '%s' ", dev_name);
        goto __exit;
    }

    dev->bus = (struct rt_i2c_bus_device *)rt_device_find(dev_name);
    if (dev->bus == RT_NULL)
    {
        LOG_E("i2c_bus %s for PCF8575 not found!", dev_name);
        goto __exit;
    }

    if (i2c_addr != RT_NULL)
        dev->i2c_addr = i2c_addr;
    else
        dev->i2c_addr = PCF8575_ADDR_DEFAULT;

    if (rt_device_open(&dev->bus->parent, RT_NULL) != RT_EOK)
    {
        LOG_D("i2c_bus %s for PCF8575 opened failed!", dev_name);
        goto __exit;
    }

    rt_device_write(&dev->bus->parent, dev->i2c_addr, &buffer, 2);

    LOG_D("pcf8575 init done", dev_name);
    return dev;

__exit:
    if (dev != RT_NULL)
        rt_free(dev);

    return RT_NULL;
}

void pcf8575_deinit(struct pcf8575_device *dev)
{
    RT_ASSERT(dev);

    rt_free(dev);
}
